
#include "stdafx.h"

#include "VideoSession.h"

#include <mipdirectshowcapture.h>
#include <mipavcodecframeconverter.h>
#include <mipavcodecencoder.h>
#include <miprtpvideoencoder.h>
#include <miprtpcomponent.h>
#include <mipaveragetimer.h>
#include <miprtpdecoder.h>
#include <miprtpvideodecoder.h>
#include <mipmediabuffer.h>
#include <mipavcodecdecoder.h>
#include <mipvideomixer.h>
#include <mipencodedvideomessage.h>
#include <miprawvideomessage.h>
#include <rtpsession.h>
#include <rtpsessionparams.h>
#include <rtperrors.h>
#include <rtpudpv4transmitter.h>

#include <log4cpp/Category.hh>

#include "videooutput.h"

#define VideoSession_ERRSTR_NOTINIT						"Not initialized"
#define VideoSession_ERRSTR_ALREADYINIT					"Already initialized"

static log4cpp::Category& Logger = log4cpp::Category::getInstance(__Module__Name+std::string(".videosession"));

VideoSession::VideoSession()
{
	zeroAll();
	m_init = false;
	m_openCamera = true;
	m_inputRunning = false;
	m_outputRunning = false;
}

VideoSession::~VideoSession()
{
	deleteAll();
}

void VideoSession::onInputThreadExit(bool err, const std::string &compName, const std::string &errStr){
	Logger.info("InputThreadExit : ComponentName[%s] ErrorString[%s]",compName.c_str(),errStr.c_str());
}

void VideoSession::onOutputThreadExit(bool err, const std::string &compName, const std::string &errStr){
	Logger.info("OutputThreadExit : ComponentName[%s] ErrorString[%s]",compName.c_str(),errStr.c_str());
}

bool VideoSession::init(const VideoSessionParams *pParams, MIPRTPSynchronizer *pSync, RTPSession *pRTPSession)
{
	if (m_init)
	{
		setErrorString(VideoSession_ERRSTR_ALREADYINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_ALREADYINIT));
		return false;
	}

	VideoSessionParams defaultParams;
	const VideoSessionParams *pParams2 = &defaultParams;

	if (pParams){
		pParams2 = pParams;
	}

	real_t frameRate = pParams2->getFrameRate();
	int width;
	int height;
	
	m_pTimer = new MIPAverageTimer(MIPTime(1.0/frameRate));

	width = pParams2->getWidth();
	height = pParams2->getHeight();
	m_pInput = new MIPDirectShowCapture();
	if (!m_pInput->open(width, height, frameRate,pParams2->getDevice()))
	{
		/** 
		 * some user do not have a camera, but we still allow user to receive other's video
		*/
		m_openCamera = false;
		//setErrorString(m_pInput->getErrorString());
		Logger.error(DEBUG_MSG(m_pInput->getErrorString()));
		//deleteAll();
		//return false;
	}

	int bandwidth = pParams2->getBandwidth();
		
	m_pAvcEnc = new MIPAVCodecEncoder();
	if (!m_pAvcEnc->init(width, height, frameRate, bandwidth))
	{
		setErrorString(m_pAvcEnc->getErrorString());
		Logger.error(DEBUG_MSG(m_pAvcEnc->getErrorString()));
		deleteAll();
		return false;
	}

	m_pRTPEnc = new MIPRTPVideoEncoder();
	if (!m_pRTPEnc->init(frameRate))
	{
		setErrorString(m_pRTPEnc->getErrorString());
		Logger.error(DEBUG_MSG(m_pRTPEnc->getErrorString()));
		deleteAll();
		return false;
	}
	
	m_pRTPEnc->setPayloadType(103);

	if (pRTPSession != 0)
	{
		int status;
		
		m_pRTPSession = pRTPSession;
		m_deleteRTPSession = false;

		if ((status = m_pRTPSession->SetTimestampUnit(1.0/90000.0)) < 0)
		{
			setErrorString(RTPGetErrorString(status));
			Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
			deleteAll();
			return false;		
		}
	}
	else
	{
		RTPUDPv4TransmissionParams transParams;
		RTPSessionParams sessParams;
		int status;
		
		transParams.SetPortbase(pParams2->getPortbase());
		sessParams.SetOwnTimestampUnit(1.0/90000.0);
		sessParams.SetMaximumPacketSize(64000);
		sessParams.SetAcceptOwnPackets(pParams2->getAcceptOwnPackets());

		m_pRTPSession = new RTPSession();
		m_deleteRTPSession = true;
		
		if ((status = m_pRTPSession->Create(sessParams,&transParams)) < 0)
		{
			setErrorString(RTPGetErrorString(status));
			Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
			deleteAll();
			return false;
		}
	}
	
	m_pRTPComp = new MIPRTPComponent();
	if (!m_pRTPComp->init(m_pRTPSession))
	{
		setErrorString(m_pRTPComp->getErrorString());
		Logger.error(DEBUG_MSG(m_pRTPComp->getErrorString()));
		deleteAll();
		return false;
	}

	m_pTimer2 = new MIPAverageTimer(MIPTime(1.0/frameRate));
	
	m_pRTPDec = new MIPRTPDecoder();
	if (!m_pRTPDec->init(true, pSync, m_pRTPSession))
	{
		setErrorString(m_pRTPDec->getErrorString());
		Logger.error(DEBUG_MSG(m_pRTPDec->getErrorString()));
		deleteAll();
		return false;
	}

	m_pRTPVideoDec = new MIPRTPVideoDecoder();
	m_pRTPDec->setDefaultPacketDecoder(m_pRTPVideoDec);

	m_pMediaBuf = new MIPMediaBuffer();
	if (!m_pMediaBuf->init(MIPTime(1.0/frameRate)))
	{
		setErrorString(m_pMediaBuf->getErrorString());
		Logger.error(DEBUG_MSG(m_pMediaBuf->getErrorString()));
		deleteAll();
		return false;
	}
	
	m_pAvcDec = new MIPAVCodecDecoder();
	if (!m_pAvcDec->init())
	{
		setErrorString(m_pAvcDec->getErrorString());
		Logger.error(DEBUG_MSG(m_pAvcDec->getErrorString()));
		deleteAll();
		return false;
	}
	
	m_pMixer = new MIPVideoMixer();
	if (!m_pMixer->init(frameRate))
	{
		setErrorString(m_pMixer->getErrorString());
		Logger.error(DEBUG_MSG(m_pMixer->getErrorString()));
		deleteAll();
		return false;
	}

	m_pVideoOutput = new librtp_video::VideoOuput();
	
	// Create input chain
	
	m_pInputChain = new InputChain(this);

	m_pInputChain->setChainStart(m_pTimer);
	m_pInputChain->addConnection(m_pTimer, m_pInput);
	m_pInputChain->addConnection(m_pInput, m_pAvcEnc);
	m_pInputChain->addConnection(m_pAvcEnc, m_pRTPEnc);
	m_pInputChain->addConnection(m_pRTPEnc, m_pRTPComp);
	
	// Create output chain

	m_pOutputChain = new OutputChain(this);

	m_pOutputChain->setChainStart(m_pTimer2);
	m_pOutputChain->addConnection(m_pTimer2, m_pRTPComp);
	m_pOutputChain->addConnection(m_pRTPComp, m_pRTPDec);
	m_pOutputChain->addConnection(m_pRTPDec, m_pMediaBuf, true);
	m_pOutputChain->addConnection(m_pMediaBuf, m_pAvcDec, true, MIPMESSAGE_TYPE_VIDEO_ENCODED, MIPENCODEDVIDEOMESSAGE_TYPE_H263P);
	m_pOutputChain->addConnection(m_pAvcDec, m_pMixer, true);
	m_pOutputChain->addConnection(m_pMixer, m_pVideoOutput, true);
	
	m_init = true;
	return true;
}

bool VideoSession::startSendChain(){

	if(NULL == m_pInputChain){
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return FALSE;
	}

	if(!m_openCamera){
		return true;
	}

	if (!m_pInputChain->start())
	{
		setErrorString(m_pInputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pInputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_outputRunning = true;

	return TRUE;
}

bool VideoSession::stopSendChain(){

	if(NULL == m_pInputChain){
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return FALSE;
	}

	if(!m_openCamera){
		return true;
	}

	if (!m_pInputChain->stop())
	{
		setErrorString(m_pInputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pInputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_outputRunning = false;

	return TRUE;
}

bool VideoSession::startReceiveChain(){

	if(NULL == m_pOutputChain){
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return FALSE;
	}

	if (!m_pOutputChain->start())
	{
		setErrorString(m_pOutputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pOutputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_inputRunning = true;

	return TRUE;
}

bool VideoSession::stopReceiveChain(){

	if(NULL == m_pOutputChain){
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return FALSE;
	}

	if (!m_pOutputChain->stop())
	{
		setErrorString(m_pOutputChain->getErrorString());
		Logger.error(DEBUG_MSG(m_pOutputChain->getErrorString()));
		deleteAll();
		return false;
	}

	m_inputRunning = false;

	return TRUE;
}

bool VideoSession::destroy()
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	if(isSessionRunning()){
		stopSession();
	}

	deleteAll();
	m_init = false;
	return true;
}

// Note: since we can't be sure that the underlying RTPTransmitter
// was compiled in a thread-safe way, we'll lock the RTP component

bool VideoSession::addDestination(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->AddDestination(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool VideoSession::deleteDestination(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;
	
	m_pRTPComp->lock();
	if ((status = m_pRTPSession->DeleteDestination(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();

	return true;
}

bool VideoSession::clearDestinations()
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	m_pRTPComp->lock();
	m_pRTPSession->ClearDestinations();
	m_pRTPComp->unlock();
	return true;
}

bool VideoSession::supportsMulticasting()
{
	if (!m_init)
		return false;
	
	m_pRTPComp->lock();
	bool val = m_pRTPSession->SupportsMulticasting();
	m_pRTPComp->unlock();

	return val;
}

bool VideoSession::joinMulticastGroup(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->JoinMulticastGroup(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::leaveMulticastGroup(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->LeaveMulticastGroup(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::leaveAllMulticastGroups()
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	m_pRTPComp->lock();
	m_pRTPSession->LeaveAllMulticastGroups();
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::setReceiveMode(RTPTransmitter::ReceiveMode m)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->SetReceiveMode(m)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::addToIgnoreList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->AddToIgnoreList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::deleteFromIgnoreList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;
	
	m_pRTPComp->lock();
	if ((status = m_pRTPSession->DeleteFromIgnoreList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::clearIgnoreList()
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	m_pRTPComp->lock();
	m_pRTPSession->ClearIgnoreList();
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::addToAcceptList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->AddToAcceptList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::deleteFromAcceptList(const RTPAddress &addr)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	int status;

	m_pRTPComp->lock();
	if ((status = m_pRTPSession->DeleteFromAcceptList(addr)) < 0)
	{
		m_pRTPComp->unlock();
		setErrorString(RTPGetErrorString(status));
		Logger.error(DEBUG_MSG(RTPGetErrorString(status)));
		return false;
	}
	m_pRTPComp->unlock();
	
	return true;
}

bool VideoSession::clearAcceptList()
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	
	m_pRTPComp->lock();
	m_pRTPSession->ClearAcceptList();
	m_pRTPComp->unlock();
	
	return true;
}

void VideoSession::zeroAll()
{
	m_pInputChain = 0;
	m_pOutputChain = 0;
	m_pInput = 0;
	m_pInputFrameConverter = 0;
	m_pAvcEnc = 0;
	m_pRTPEnc = 0;
	m_pRTPComp = 0;
	m_pRTPSession = 0;
	m_pTimer = 0;
	m_pTimer2 = 0;
	m_pRTPDec = 0;
	m_pRTPVideoDec = 0;
	m_pMediaBuf = 0;
	m_pAvcDec = 0;
	m_pMixer = 0;
	m_pVideoOutput = 0;

	m_init = false;
	m_openCamera = false;
}

void VideoSession::deleteAll()
{
	if (m_pInputChain)
	{
		m_pInputChain->stop();
		delete m_pInputChain;
	}
	if (m_pOutputChain)
	{
		m_pOutputChain->stop();
		delete m_pOutputChain;
	}
	if (m_pInput)
		delete m_pInput;
	if (m_pVideoOutput)
		delete m_pVideoOutput;
	if (m_pTimer)
		delete m_pTimer;
	if (m_pAvcEnc)
		delete m_pAvcEnc;
	if (m_pRTPEnc)
		delete m_pRTPEnc;
	if (m_pRTPComp)
		delete m_pRTPComp;
	if (m_pRTPSession)
	{
		if (m_deleteRTPSession)
		{
			m_pRTPSession->BYEDestroy(RTPTime(2,0),0,0);
			delete m_pRTPSession;
		}
	}
	if (m_pTimer2)
		delete m_pTimer2;
	if (m_pRTPDec)
		delete m_pRTPDec;
	if (m_pRTPVideoDec)
		delete m_pRTPVideoDec;
	if (m_pMediaBuf)
		delete m_pMediaBuf;
	if (m_pAvcDec)
		delete m_pAvcDec;
	if (m_pMixer)
		delete m_pMixer;
	zeroAll();
}

bool VideoSession::getSourceIDs(std::list<uint64_t> &sourceIDs)
{
	if (!m_init)
	{
		setErrorString(VideoSession_ERRSTR_NOTINIT);
		Logger.error(DEBUG_MSG(VideoSession_ERRSTR_NOTINIT));
		return false;
	}
	return true;
}

bool VideoSession::startSession(){
	return (startSendChain() && startReceiveChain());
}

bool VideoSession::stopSession(){
	return (stopSendChain() && stopReceiveChain());
}
